/**
 * @file    main.c
 * @author  Thomas Di'Meco and Matthieu Maugard
 * @brief   Start the program
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "assembler.h"
#include "processor.h"
#include "memory.h"
#include "errors.h"


// Private members
int emu_procsi (int argc, char * argv[]);
int get_arguments (int argc, char * argv[],
                   char ** filename, int * step_by_step, int * show_usage);
void show_usage_message ();
void show_version_message ();


/**
 * EmuProcsi program start
 */
int emu_procsi (int argc, char * argv[])
{
    // Get program parameters
    char * filename;
    int step_by_step;
    int show_message;

    // Check arguments
    if (get_arguments(argc, argv, &filename, &step_by_step, &show_message))
    {
        printf("Run 'emu-procsi --help' to display the usage message\n");
        return ERROR_INVALID_ARGUMENT;
    }

    // Show usage message if requested
    if (show_message == 1)
    {
        show_usage_message();
        return NO_ERROR;
    }
    // Show version message if requested
    else if (show_message == 2)
    {
        show_version_message();
        return NO_ERROR;
    }

    // Assemble file
    printf("Assembling %s...\n", filename);
    int err_asm = assemble(filename, stderr);

    // Check assembly error code
    if (err_asm)
    {
        printf("Assembly failed\n");
        return ERROR_ASSEMBLY_FAILED;
    }
    else
        printf("Assembly finished\n");


    // Error id
    int err = 0;

    // Start the program execution
    if (step_by_step)
        printf("Program started step by step...\n");
    else
        printf("Program started...\n");

    /* Program loop: execute next instruction until the end (ie. err != 0)
     * err = 0 means no error (and continue to execute the next instruction)
     * err < 0 means halt (and stop the program)
     * err > 0 means error (err contains the error id)
     */
    while( !(err = execute(step_by_step, stdout, stdin)) );

    // Display "error" message
    printf("Program terminated: %s\n", error_string(err));

    // Dump registers and memory before exiting if not step by step
    if (!step_by_step)
    {
        printf("Dumping registers and memory before exiting\n");
        dump_reg();
        dump_mem();
    }

    // Return error id (if err > 0, obviously)
    return (err > 0 ? err : 0);
}


/**
 * Get program parameters from arguments
 * @param argc         The main argc of the program
 * @param argv         The main argv of the program
 * @param filename     The filename given, or the default filename otherwise
 * @param step_by_step Boolean that indicate if the program
 *                     have to run step by step
 * @param show_message Boolean that indicate if the user request the usage message (1)
 *                     or the version message (2)
 * @return An error code != 0 if an error occured, 0 if no error
 */
int get_arguments (int argc, char * argv[],
                   char ** filename, int * step_by_step, int * show_message)
{
    // Default values
    *filename     = 0;
    *step_by_step = 1;
    *show_message = 0;

    // Parse arguments
    int i;
    for (i = 1; i < argc; i++)
    {
        char * arg = argv[i];

        // Step by step argument
        if (!strcmp(arg, "-s")) *step_by_step = 0;
        // Show usage argument
        else if (!strcmp(arg, "--help"))
        {
            *show_message = 1;
            return 0;
        }
        // Show version argument
        else if (!strcmp(arg, "--version"))
        {
            *show_message = 2;
            return 0;
        }
        // Filename argument
        else *filename = arg;
    }

    // Check mandatory arguments
    if (*filename == 0)
    {
        printf("Error: filename not specified\n");
        return 1;
    }

    // Returns no error code
    return 0;
}


/**
 * Show usage message to the standard output
 */
void show_usage_message ()
{
    printf(
        "USAGE\n"
        "    emu-procsi [-s] filename\n"
        "    emu-procsi --version\n"
        "    emu-procsi --help\n"
        "\n"
        "DESCRIPTION\n"
        "    -s        : Do not run the PROCSI program step by step\n"
        "    --version : Display the program version\n"
        "    --help    : Display this usage help\n"
    );
}


/**
 * Show version message to the standard output
 */
void show_version_message ()
{
    printf(
        "PROCSI emulator program - version 1.0\n"
        "Author: Thomas Di'Meco and Matthieu Maugard\n"
    );
}


int main (int argc, char * argv[])
{
    return emu_procsi(argc, argv);
}
